iT邦幫忙

2026 iThome 鐵人賽

DAY 23
0
自我挑戰組

用數據守護雙眼:生活型態對視力影響的探索性資料分析系列 第 23 篇

輕量急速王者!LightGBM 葉節點導向生長與多類別預測實戰

  • 分享至 

  • xImage
  •  

針對 Day 22 XGBoost 在全域損失最小化過程中過度吸附中度風險、導致低度風險預測歸零(Macro F1 跌至 0.248)的現象,導入微軟開發的另一梯度提升巨頭——LightGBM(Light Gradient Boosting Machine)。運用其特有的葉節點導向生長、基於直方圖的特徵分箱(Histogram-based Binning)以及多類別權重平衡參數,探討 LightGBM 能否在極速訓練的同時,修復極端風險等級的召回平衡性。

一、 實作前情境:為什麼在 XGBoost 之後還要嘗試 LightGBM?
在 Day 22 中,XGBoost 拿下了全場最高的 43.00% 總準確率,但混淆矩陣揭示它把低度風險(Low Risk)預測歸零,將大量樣本吸入中度風險。

LightGBM 與 XGBoost 雖然同屬梯度提升家族,但底層架構存在核心差異:

1.生長策略差異(Level-wise vs. Leaf-wise):

  • XGBoost 預設採用層級生長,同一層的所有節點一併分裂,容易產生冗餘分支。
  • LightGBM 採用葉節點導向生長,每次只挑選「能帶來最大分裂增益」的單一葉節點進行分裂。在特徵重疊的資料集上,能用更少的葉節點精準切開高資訊量區域。

2.類別非平衡感知與直方圖加速:

  • LightGBM 原生支援將連續特徵分箱為直方圖(Discrete Bins),能有效減少微小量測雜訊對分裂點的干擾,對於自覺量表這種連續但包含雜訊的特徵具備平滑防過擬合效果。
  • 透過調整 class_weight='balanced',強制約束模型在多分類情境下關注少數類(低度與高度風險)。
# ==========================================
# Day 23:LightGBM 多分類模型實戰與性能對比
# ==========================================

import warnings
warnings.filterwarnings('ignore')

import matplotlib.pyplot as plt
import seaborn as sns
import matplotlib.font_manager as fm
import pandas as pd
import numpy as np

import lightgbm as lgb
from sklearn.metrics import classification_report, confusion_matrix, accuracy_score, f1_score

# 1. 引用中文字型
font_path = 'NotoSansTC-Regular.otf'
my_font = fm.FontProperties(fname=font_path)

# 2. 建立並訓練 LightGBM 分類器
# objective='multiclass', num_class=3
lgb_clf = lgb.LGBMClassifier(
    objective='multiclass',
    num_class=3,
    n_estimators=100,
    learning_rate=0.05,
    max_depth=4,              # 淺層控制避免小樣本過擬合
    num_leaves=15,            # 控制葉節點上限
    min_child_samples=15,     # 每個葉節點最少樣本數
    class_weight='balanced',  # 關鍵:平衡三類別權重,打破多數類吸附
    random_state=42,
    verbose=-1
)

lgb_clf.fit(X_train, y_train)

# 3. 測試集盲測預測
y_pred_lgb = lgb_clf.predict(X_test)
y_prob_lgb = lgb_clf.predict_proba(X_test)

acc_lgb = accuracy_score(y_test, y_pred_lgb)
macro_f1_lgb = f1_score(y_test, y_pred_lgb, average='macro')

print(f"=== LightGBM 測試集整體準確率 (Accuracy): {acc_lgb * 100:.2f}% ===")
print(f"=== LightGBM Macro Average F1-score: {macro_f1_lgb:.4f} ===\n")

# 4. 輸出詳細分類評估報告 (Precision, Recall, F1-score)
target_names = ['低度風險 (Low)', '中度風險 (Medium)', '高度風險 (High)']
report_dict_lgb = classification_report(y_test, y_pred_lgb, target_names=target_names, output_dict=True)
report_df_lgb = pd.DataFrame(report_dict_lgb).transpose()
print("=== LightGBM 分類性能評估報告 (Classification Report) ===")
display(report_df_lgb.round(3))

# 5. 繪製 1x2 畫布:混淆矩陣熱力圖 + 特徵重要度 (Split Count)
fig, axes = plt.subplots(1, 2, figsize=(16, 6))
sns.set_theme(style="white")

# --- 圖 1:LightGBM 測試集混淆矩陣 ---
cm_lgb = confusion_matrix(y_test, y_pred_lgb)
sns.heatmap(
    cm_lgb, 
    annot=True, 
    fmt='d', 
    cmap='PuBuGn', 
    xticklabels=target_names, 
    yticklabels=target_names, 
    cbar=False, 
    ax=axes[0]
)
axes[0].set_title(f'LightGBM 測試集混淆矩陣\nMacro F1: {macro_f1_lgb:.3f}', fontproperties=my_font, fontsize=13, fontweight='bold')
axes[0].set_xlabel('模型預測等級 (Predicted Label)', fontproperties=my_font, fontsize=12)
axes[0].set_ylabel('真實風險等級 (True Label)', fontproperties=my_font, fontsize=12)
axes[0].set_xticklabels(target_names, fontproperties=my_font, fontsize=10)
axes[0].set_yticklabels(target_names, fontproperties=my_font, fontsize=10)

# --- 圖 2:LightGBM 特徵重要度 (Feature Importance - Split 次數) ---
importances_lgb = pd.Series(lgb_clf.feature_importances_, index=X_train.columns).sort_values(ascending=True)

importances_lgb.plot(
    kind='barh', 
    ax=axes[1], 
    color='#16a085', 
    edgecolor='none', 
    alpha=0.85
)
axes[1].set_title('LightGBM 特徵重要度排序 (Split 分裂次數)', fontproperties=my_font, fontsize=14, fontweight='bold')
axes[1].set_xlabel('特徵被選為分裂節點的次數 (Split Count)', fontproperties=my_font, fontsize=12)
axes[1].set_ylabel('輸入特徵項目', fontproperties=my_font, fontsize=12)

plt.tight_layout()
plt.show()

# 6. 輸出詳細特徵重要度表格
lgb_feat_df = pd.DataFrame({
    '特徵項目': importances_lgb.index,
    '分裂次數 (Split)': importances_lgb.values
}).sort_values(by='分裂次數 (Split)', ascending=False).reset_index(drop=True)

print("=== LightGBM 特徵分裂次數完整排序表 ===")
display(lgb_feat_df)

https://ithelp.ithome.com.tw/upload/images/20261009/20178794Hgzc7LLEWl.png
https://ithelp.ithome.com.tw/upload/images/20261009/20178794dDItvVyx50.png


上一篇
梯度提升王牌出擊!XGBoost 多分類殘差迭代與高風險召回攻防
下一篇
五大演算法終極爭霸!全模型性能評測與多類別 ROC-AUC 曲線大對決
系列文
用數據守護雙眼:生活型態對視力影響的探索性資料分析 共 26 篇
圖片
  熱門推薦
圖片
{{ item.channelVendor }} | {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

尚未有邦友留言

立即登入留言